#light

namespace Langexplr.Abc

open Langexplr.Abc
open System.IO
open InstructionPatterns
open Langexplr.Abc.InstructionUtilities


module InstructionManipulation = begin
    let rec CalculateBranchOffsets(code : AbcFileInstruction array,
                                   fileCreator  : AbcFileCreator,
                                   currentOffset:int64,
                                   currentInstruction:int,
                                   offsets:Map<string,int64>) =
        if (code.Length > currentInstruction) then
           let newOffsets =
                match code.[currentInstruction] with
                | ArtificialCodeBranchLabel name -> 
                      Map.add name currentOffset offsets
                | _ -> offsets 
           in
           let size = code.[currentInstruction].GetSize(fileCreator) 
           in
               CalculateBranchOffsets(code,
                                      fileCreator,
                                      int64(size) + currentOffset,
                                      currentInstruction + 1,
                                      newOffsets)
        else
           offsets
  
    let AdjustSwitchOpcode defaultCase cases (labelOffsets:Map<string,int64>) offset =
       let adjust = 
            fun case -> 
                  match case with
                  | SolvedReference label ->
                           UnSolvedReference <| int(labelOffsets.[label]) -
                                                offset
                  | _ -> case 
       in           
         LookupSwitch(adjust defaultCase,Array.map adjust cases)

    let ConvertSymbolicLabelsToByteReferences(fileCreator:AbcFileCreator) (code:AbcFileInstruction array)=
       let labelOffsets: Map<string,int64>  =
                 CalculateBranchOffsets(code,
                                        fileCreator,
                                        int64(0),0,Map.empty)  in
       code 
          |> Array.toList
          |> List.fold 
                     (fun result i -> 
                          match result with
                          | (prevOffset,
                             prevI:AbcFileInstruction)::_ -> 
                                    (prevI.GetSize(fileCreator) + prevOffset,i)::result
                          | [] -> [(0,i)]) []
          |> List.rev
          |> List.map 
                   (fun i -> 
                        match i with 
                        |  (offset,
                            instr & SolvedSingleBranchInstruction(
                                      label,constructFunction))->
                           let targetOffset =
                                   int(labelOffsets.[label]) - (offset+instr.GetSize(fileCreator))  in
                            (offset,constructFunction(
                                        UnSolvedReference(targetOffset)))
                        | (offset,
                           LookupSwitch(defaultCase,cases)) ->
                             (offset, AdjustSwitchOpcode 
                                           defaultCase cases labelOffsets offset) 
                        | _ -> i)
          |> List.map (fun (_,instruction) -> instruction)
          |> List.toArray


end


type AvmType = interface
 end
   
 
type AvmClassMember() =
  class
 end 

type AvmVariable =
  class
  
  inherit AvmClassMember
end

type AvmMemberClassMember(name : QualifiedName) = class
  inherit AvmClassMember()
  member this.ClassName = name
end
  

type AvmConstantKindValue =
  | OptionalInt of int
  | OptionalUInt of uint32
  | OptionalDouble of double
  | OptionalString of string
  | OptionalBoolean of bool
  | OptionalNull
  | OptionalUndefined
  | OptionalNamespace of Ns 
  | OptionalPackageNamespace of Ns 
  | OptionalPackageInternalNs of Ns 
  | OptionalProtectedNs of Ns 
  | OptionalExplicitNamespace of Ns 
  | OptionalStaticProtectedNs of Ns 
  | OptionalPrivateNs of Ns 
with
   static member Create(constantKind:byte,valIndex:int,cpool : ConstantPoolInfo) =
      match int(constantKind) with
      | 0x03 -> OptionalInt(cpool.GetInt(valIndex))
      | 0x04 -> OptionalUInt(cpool.GetUInt(valIndex))
      | 0x06 -> OptionalDouble(cpool.GetDouble(valIndex))
      | 0x01 -> OptionalString(cpool.GetString(valIndex))
      | 0x0B -> OptionalBoolean(true)
      | 0x0A -> OptionalBoolean(false)
      | 0x0C -> OptionalNull
      | 0x00 -> OptionalUndefined
      | 0x08 -> OptionalNamespace(cpool.SolveNamespaceName(valIndex))
      | 0x16 -> OptionalPackageNamespace(cpool.SolveNamespaceName(valIndex))
      | 0x17 -> OptionalPackageInternalNs(cpool.SolveNamespaceName(valIndex)) 
      | 0x18 -> OptionalProtectedNs(cpool.SolveNamespaceName(valIndex)) 
      | 0x19 -> OptionalExplicitNamespace(cpool.SolveNamespaceName(valIndex))
      | 0x1A -> OptionalStaticProtectedNs(cpool.SolveNamespaceName(valIndex)) 
      | 0x05 -> OptionalPrivateNs(cpool.SolveNamespaceName(valIndex)) 
      | _ -> raise (new System.InvalidOperationException("Could not find constant kind"))
      
   member this.ToLowerIr(fileCreator : AbcFileCreator) =
     let cpCreator = fileCreator.ConstantPoolCreator in
      match this with
      | OptionalInt(intValue) -> 0x03, cpCreator.GetIntIndex(intValue)
      | OptionalUInt(uIntValue) -> 0x04, cpCreator.GetUIntIndex(uIntValue)
      | OptionalDouble(doubleValue) -> 0x06, cpCreator.GetDoubleIndex(doubleValue)
      | OptionalString(value) -> 0x01, cpCreator.AddString(value)
      | OptionalBoolean(true) -> 0x0B, 0
      | OptionalBoolean(false) -> 0x0A,0
      | OptionalNull -> 0x0C, 0
      | OptionalUndefined -> 0x00,0
      | OptionalNamespace(Ns(name,kind)) -> 
                          0x08, cpCreator.GetNamespace(name,kind)
      | OptionalPackageNamespace(Ns(name,kind)) -> 
                          0x16, cpCreator.GetNamespace(name,kind)
      | OptionalPackageInternalNs(Ns(name,kind))  -> 
                          0x17, cpCreator.GetNamespace(name,kind)
      | OptionalProtectedNs(Ns(name,kind))  -> 
                          0x18, cpCreator.GetNamespace(name,kind)
      | OptionalExplicitNamespace(Ns(name,kind)) -> 
                          0x19, cpCreator.GetNamespace(name,kind)
      | OptionalStaticProtectedNs(Ns(name,kind))  -> 
                          0x1A, cpCreator.GetNamespace(name,kind)
      | OptionalPrivateNs(Ns(name,kind)) -> 
                          0x05 , cpCreator.GetNamespace(name,kind)
      
end

type AvmMethodParameter(typeName : QualifiedName, name: string Option, optionalValue : AvmConstantKindValue Option) =
   class
   member this.Name
     with get() = name
   member this.OptionalValue
     with get() = optionalValue
   member this.TypeName
     with get() = typeName
     
end


type AvmMethodBody(maxStack : int,
                   localCount : int,
                   initScopeDepth : int,
                   maxScopeDepth : int,
                   code : AbcFileInstruction array,
                   exceptions : AbcExceptionInfo array,
                   traits : AbcTraitsInfo array) =
  class
    member this.LocalCount with get() = localCount
    member this.MaxStack with get() = maxStack
    member this.InitScopeDepth with get() = initScopeDepth
    member this.MaxScopeDepth with get() = maxScopeDepth
    member this.Instructions with get() = code
    member this.Exceptions with get() = exceptions
    member this.Traits with get() = traits
         
    static member GetInstructions(code:byte array,
                                  constantPool:ConstantPoolInfo,
                                  classNames : QualifiedName array) =
       using(new BinaryReader(new MemoryStream(code))) (fun aInput ->
          AvmMethodBody.ReadAndProcessInstructions(aInput , 
                                                   code.Length,
                                                   constantPool,
                                                   classNames))

    
    static member ReadAndProcessInstructions(aInput:BinaryReader,
                                             count,
                                             constantPool:ConstantPoolInfo,
                                             classNames : QualifiedName array) = 
        let instructionsAndOffsets = 
                    (AvmMethodBody.ReadingInstructions([],
                                                       aInput,
                                                       count,
                                                       constantPool,
                                                       classNames)) in        
        let destinations = 
                    AvmMethodBody.CollectDestinations(instructionsAndOffsets,
                                                      Map.empty,
                                                      instructionsAndOffsets)
        in
           AvmMethodBody.UpdateCodeWithDestinations(
                                         destinations,
                                         instructionsAndOffsets,[]) |> List.toArray

           
    static member IsDestinationDefined(destination:int,
                                       instructions:(int64*AbcFileInstruction) list) =
       List.exists (fun (offset,_) -> int64(destination) = offset) instructions 

    static member CheckLookupSwitchCase  (baseOffset:int)
                                         (totalInstructions:(int64*AbcFileInstruction)
                                         list) =
       fun (destinations:Map<int64,string>) target  ->
          match target with
          | UnSolvedReference(relativeOffset) when
              (AvmMethodBody.IsDestinationDefined(int(baseOffset+relativeOffset),
                                                  totalInstructions)) ->
                    destinations.Add(int64(baseOffset + relativeOffset),
                                     sprintf "dest%d" (baseOffset+relativeOffset))
          | _ -> destinations

    static member  CollectDestinations(instructions:(int64*AbcFileInstruction) list, 
                                       destinations:Map<int64,string>,
                                       totalInstructions:(int64*AbcFileInstruction) list) =
       match instructions with 
       | ((UnsolvedAbsoluteBranchInstruction(offset,_))::rest) when 
                    (AvmMethodBody.IsDestinationDefined(int(offset),
                                                        totalInstructions)) -> 
              AvmMethodBody.CollectDestinations(rest,
                                       destinations.Add(offset,
                                                        sprintf "dest%d" offset),
                                       totalInstructions)
       | ((offset,(LookupSwitch(defaultBranch,cases)))::rest) ->
              let baseOffset = int(offset) in
               AvmMethodBody.CollectDestinations(rest,
                                       Seq.append [defaultBranch] cases |>                                        
                                       Seq.fold (AvmMethodBody.CheckLookupSwitchCase baseOffset totalInstructions ) destinations,
                                       totalInstructions)
       | (_::rest) -> AvmMethodBody.CollectDestinations(rest,
                                                        destinations,
                                                        totalInstructions)
       | [] -> destinations
              

    static member SolveSwitchCase case baseOffset (destinations:Map<int64,string>) =
       match case with
       | UnSolvedReference(offset) when
                destinations.ContainsKey(baseOffset+int64(offset)) ->
             SolvedReference(destinations.[baseOffset+int64(offset)])
       | _ -> case

    static member UpdateCodeWithDestinations(destinations:Map<int64,string>,
                                             instructions,
                                             resultingInstructions) =
          
          let processedInstructions = 
                match instructions with
                | (((offset,_) & 
                    UnsolvedAbsoluteBranchInstruction( jumpOffset,f))::rest) 
                             when (destinations.ContainsKey(jumpOffset)) ->
                                (offset,
                                  f(SolvedReference(destinations.[jumpOffset])))::rest
                | ((offset,LookupSwitch(defaultCase,cases))::rest) ->                 
                   (offset,
                      LookupSwitch(AvmMethodBody.SolveSwitchCase defaultCase offset destinations,
                                   Array.map (fun c->AvmMethodBody.SolveSwitchCase c offset destinations) cases))::rest
                | _ -> instructions
          in                                                                                      
             match processedInstructions with         
             | ((offset,instruction)::rest) when (destinations.ContainsKey(int64(offset))) ->
                 AvmMethodBody.UpdateCodeWithDestinations(destinations,
                                                 rest,
                                                 instruction::ArtificialCodeBranchLabel(destinations.[int64(offset)])::resultingInstructions)
             | ((_,instruction)::rest)  ->
                 AvmMethodBody.UpdateCodeWithDestinations(destinations,
                                                 rest,
                                                 instruction::resultingInstructions)
             | [] -> List.rev(resultingInstructions)

       
    
    static member ReadingInstructions(result:(int64 * AbcFileInstruction) list, 
                                      reader:BinaryReader, 
                                      codeSize : int,
                                      constantPool:ConstantPoolInfo,
                                      classNames : QualifiedName array )  =
       if reader.BaseStream.Position >= int64(codeSize) then
          List.rev(result)
       else
          AvmMethodBody.ReadingInstructions( (reader.BaseStream.Position,
                                              AbcFileInstruction.ReadFrom(reader,constantPool,classNames))::result,
                                             reader,
                                             codeSize,
                                             constantPool,classNames)

       
    static member Create(body : AbcMethodBodyInfo,
                         constantPool:ConstantPoolInfo,
                         classNames : QualifiedName array) : AvmMethodBody = 
       let instructions = AvmMethodBody.GetInstructions(body.InstructionBytes,
                                                        constantPool,
                                                        classNames) 
       in
             AvmMethodBody(body.MaxStack,
                           body.LocalCount,
                           body.InitScopeDepth,
                           body.MaxScopeDepth,
                           instructions,
                           body.Exceptions,body.Traits)
       


                      
    member this.ToLowerIr(fileCreator : AbcFileCreator) =
       
       let instr = InstructionManipulation.ConvertSymbolicLabelsToByteReferences fileCreator code
                    
       let instructionBytes = ConvertToByteArray(instr,fileCreator) 
       in
          AbcMethodBodyInfo(-1,
                            maxStack,
                            localCount,
                            initScopeDepth,
                            maxScopeDepth,
                            instructionBytes,
                            exceptions,
                            traits)
end

type AvmMethod(name:string,
               returnType:QualifiedName, 
               parameters : AvmMethodParameter array, 
               body : AvmMethodBody Option ) =
  class  
  let name = name
  member this.Name
     with get() = name

  member this.ReturnType
     with get() = returnType
     
  member this.Parameters 
     with get() = parameters
     
  member this.Body
     with get() = body     
 
  member this.CloneWithBody(newbody : AvmMethodBody) = 
    AvmMethod(name,returnType,parameters,Some newbody)
     
  member this.ToLowerIr(fileCreator : AbcFileCreator) =  
    (let constantPoolCreator = fileCreator.ConstantPoolCreator in
       {  paramCount = parameters.Length;
          returnType = constantPoolCreator.GetNameEntry(returnType);
          paramType = Array.map (fun (p:AvmMethodParameter) -> constantPoolCreator.GetNameEntry(p.TypeName)) parameters;
          name = constantPoolCreator.AddString(name);
          flags = byte(0);
          options =  { count = 0; detail = [||]} ;
          paramNames = [||] },
     if (body.IsSome) then  
          Some(body.Value.ToLowerIr(fileCreator))
       else 
          None)
  
        
  static member Create(methodInfo : AbcMethodInfo,
                       cpoolInfo:ConstantPoolInfo, 
                       body : AbcMethodBodyInfo Option,
                       classNames : QualifiedName array) :AvmMethod =
     let name = cpoolInfo.GetString( methodInfo.name )
     let returnType = cpoolInfo.SolveTypeName( methodInfo.returnType)
     let optNonOptDiff  = methodInfo.paramCount - methodInfo.options.count in
     let optionalParams = 
         [| for index in 0 .. methodInfo.paramCount-1 ->
            if (index < optNonOptDiff )  then
               None
            else 
               let (i,k) = methodInfo.options.detail.[index - optNonOptDiff ] in
               Some (AvmConstantKindValue.Create(k,i,cpoolInfo))            
         |]
     let paramNames = 
            if ((Array.length methodInfo.paramNames) > 0) then
               (Array.map (fun nIndex -> Some (cpoolInfo.SolveMultiName(nIndex).ToString())) methodInfo.paramNames)
            else 
               (Array.create methodInfo.paramCount None)          
           //,methodInfo.flags &&& 0x80 <> 0) with            
     let parameters =
        [|for i in 0 .. methodInfo.paramCount-1 -> 
           AvmMethodParameter(cpoolInfo.SolveTypeName(methodInfo.paramType.[i]), 
                              paramNames.[i], 
                              optionalParams.[i]) |] 
     
     let bodyContents = 
        match body with
          | Some(bodyInfo) -> Some(AvmMethodBody.Create(bodyInfo,cpoolInfo,classNames))
          | None -> None
     
     in     
      AvmMethod(name,returnType,parameters,bodyContents)
end

type AvmMemberMethod(name:QualifiedName,cmethod:AvmMethod,attributes:AbcTraitAttribute) =   
   class  
   inherit AvmClassMember()
   let cmethod = cmethod
   let name = name
   
   member this.Method 
       with get() = cmethod
       
   member this.Name 
       with get() = name       

   member this.Attributes 
       with get() = attributes       

end

type AvmMemberProperty(name:QualifiedName,
                       getter : AvmMemberMethod Option,
                       setter : AvmMemberMethod Option) = class
     inherit AvmClassMember()

     member this.Name with get() = name
     member this.Getter with get() = getter
     member this.Setter with get() = setter       
end


type AvmMemberSlot(name:QualifiedName, 
                   slotType:QualifiedName, 
                   initialValue : AvmConstantKindValue option) =
  class
  inherit AvmClassMember()
  
  member this.Name
    with get() = name
    
  member this.Type
    with get() = slotType
    
  member this.InitialValue
    with get() = initialValue
    
  member this.GetInitialValueLowerIr(fileCreator:AbcFileCreator) =
     match initialValue with
     | Some(value) -> value.ToLowerIr(fileCreator)
     | None -> (0,0)
    
  static member Create(nameIndex : int,slot:TraitType,constantPool:ConstantPoolInfo) =
     match slot with
        | TraitSlot(slotIdIndex,typeNameIndex,vIndex,vKind) ->
               let typeName = constantPool.SolveMultiName(typeNameIndex)
               let  name = constantPool.SolveMultiName(nameIndex)
               let initialValue = if vIndex <> 0 then
                                     Some(
                                       AvmConstantKindValue.Create(
                                              vKind,
                                              vIndex,
                                              constantPool))                                     
                                  else
                                     None
               AvmMemberSlot(name,typeName,initialValue)
        | _ -> raise (new System.InvalidOperationException("Could not create slot with specified trait type"))
    
end

type AvmClass(name:QualifiedName,
              superClassName:QualifiedName,              
              initializer:AvmMethod,
              classInitializer:AvmMethod,
              slots : AvmMemberSlot list,
              methods : AvmMemberMethod list,
              properties : AvmMemberProperty list,
              protectedNamespace : Ns option ) = 
  class    
   let name = name
   let superClassName=superClassName
   //let Methods = methods
   
   member this.Name
     with get() = name
   member this.SuperClassName
     with get() = superClassName
     
   member this.Methods
     with get() = methods

   member this.Slots
     with get() = slots

   member this.Properties
     with get() = properties

     
   member this.Initializer 
     with get() = initializer
   member this.ClassInitializer with get() = classInitializer
   member this.ProtectedNamespace with get() = protectedNamespace
   member this.CreateSlotTraits(fileCreator: AbcFileCreator) =
     let constantPoolCreator = fileCreator.ConstantPoolCreator in
      List.map (fun (f:AvmMemberSlot) -> 
                  let vKind,vIndex = f.GetInitialValueLowerIr(fileCreator) in
                    {name = constantPoolCreator.GetNameEntry(f.Name);
                     traitType = 
                         TraitSlot(0, // TODO IMPLEMENT OTHER KINDS OF POSITIONING
                                   constantPoolCreator.GetNameEntry(f.Type),
                                   vIndex, 
                                   byte(vKind))
                     metadata = [||] // TODO IMPLEMENT THIS 
                     attributes = AbcTraitAttribute.Nothing
                    }) slots


   member this.CreateFunctionTrait(fileCreator : AbcFileCreator,
                                   name : int,
                                   typeConstructor : int*int -> TraitType,
                                   attributes:AbcTraitAttribute,
                                   theMethod : AvmMethod) =
     let constantPoolCreator = fileCreator.ConstantPoolCreator in
                     {name = name
                      traitType = 
                         typeConstructor(
                                  0, // TODO IMPLEMENT THIS                     
                                  fileCreator.AddMethodInformationWithBody(                                        
                                                  theMethod.ToLowerIr(fileCreator)))
                      metadata = [||] // TODO IMPLEMENT THIS
                      attributes = attributes
                      } 
   
   member this.CreateMethodTraits(fileCreator : AbcFileCreator) =
     let constantPoolCreator = fileCreator.ConstantPoolCreator in
       List.map (fun (m:AvmMemberMethod) -> 
                     this.CreateFunctionTrait(fileCreator,
                                              constantPoolCreator.GetNameEntry(m.Name),
                                              TraitMethod,
                                              m.Attributes,
                                              m.Method )) methods

   member this.CreateGetterSetterTraits(fileCreator : AbcFileCreator) =
     let constantPoolCreator = fileCreator.ConstantPoolCreator in
       List.map (fun (m:AvmMemberProperty) -> 
                  List.append(
                     match m.Getter with 
                     | Some(m) ->
                         [this.CreateFunctionTrait(
                                         fileCreator,
                                         constantPoolCreator.GetNameEntry(m.Name),
                                         TraitGetter,
                                         m.Attributes,
                                         m.Method )]
                     | _ -> [])
                     (
                     match m.Setter with 
                     | Some(m) ->
                         [this.CreateFunctionTrait(
                                         fileCreator,
                                         constantPoolCreator.GetNameEntry(m.Name),
                                         TraitSetter,
                                         m.Attributes,
                                         m.Method )]
                     | _ -> [])
                      ) properties
        |> List.concat

   
   member private this.GetProtectedNsFlag() =
      if (Option.isSome(protectedNamespace)) then 
         byte(ClassFlags.ProtectedNs)
      else 
         byte(0)

   member private this.GetProtectedNsIndex(fileCreator : AbcFileCreator) =
      match protectedNamespace with
      | Some(Ns(name,kind)) -> 
           fileCreator.ConstantPoolCreator.GetNamespace(name,kind)
      | None -> raise <| new System.InvalidOperationException("No protected namespace")
     
   member this.ToLowerIr(fileCreator : AbcFileCreator) =  
    (let constantPoolCreator = fileCreator.ConstantPoolCreator in
     let methodTraits = this.CreateMethodTraits(fileCreator) in
     let propertyTraits = this.CreateGetterSetterTraits(fileCreator) in
     let slotTraits = this.CreateSlotTraits(fileCreator) in
       {  name  = constantPoolCreator.GetNameEntry(name);
          superName = constantPoolCreator.GetNameEntry(superClassName);
          flags = byte(byte(ClassFlags.Sealed) |||  // TODO Implement this 
                       this.GetProtectedNsFlag()); // TODO IMPLEMENT THIS
          protectedNs = this.GetProtectedNsIndex(fileCreator); 
          intrfCount  = 0;// TODO IMPLEMENT THIS
          interfaces = [||]; // TODO IMPLEMENT THIS
          iInit = fileCreator.AddMethodInformationWithBody(initializer.ToLowerIr(fileCreator));
          traitCount = propertyTraits.Length + methodTraits.Length + 
                       slotTraits.Length; //// TODO IMPLEMENT OTHER KINDS OF SLOTS
          traits = 
                   List.concat [methodTraits ; slotTraits ; propertyTraits] 
                         |> List.toArray },
     {cInit = fileCreator.AddMethodInformationWithBody(classInitializer.ToLowerIr(fileCreator)); // TODO IMPLEMENT THIS
      traits = [||]}  // TODO IMPLEMENT THIS
      )

   static member CreateMemberMethod(name:QualifiedName, 
                                    index:int,
                                    cp:ConstantPoolInfo,
                                    classNames : QualifiedName array,
                                    methodInfo : AbcMethodInfo,
                                    methodBodies : AbcMethodBodyInfo array,
                                    attributes : AbcTraitAttribute) =
      AvmMemberMethod(name,
                      AvmMethod.Create(
                           methodInfo,
                           cp,
                           (Array.tryFind(
                                     fun(e:AbcMethodBodyInfo) -> 
                                              e.MethodId = index) methodBodies),
                           classNames),
                           attributes)

   static member CreateMemberProperty(propInfo : AbcTraitsInfo list,
                                      cp:ConstantPoolInfo,
                                      classNames : QualifiedName array, 
                                      methodInfo : AbcMethodInfo array,
                                      methodBodies : AbcMethodBodyInfo array) =
         match propInfo with
         | [t1&{traitType = TraitGetter(_,index)}] ->
             let name = cp.SolveMultiName(t1.name)
             in
                AvmMemberProperty(name,
                                  Some(AvmClass.CreateMemberMethod(name,
                                                                   index,
                                                                   cp,
                                                                   classNames,
                                                                   methodInfo.[index],
                                                                   methodBodies,
                                                                   t1.attributes)),
                                  None)
         | [t1&{traitType = TraitSetter(_,index)}] ->
             let name = cp.SolveMultiName(t1.name)
             in
                AvmMemberProperty(name,
                                  None,
                                  Some(
                                    AvmClass.CreateMemberMethod(name,
                                                               index,
                                                               cp,
                                                               classNames,
                                                               methodInfo.[index],
                                                               methodBodies,
                                                               t1.attributes)))
         | [t1&{traitType = TraitGetter(_,index1)} ;
            t2&{traitType = TraitSetter(_,index2)} ] ->
             let name = cp.SolveMultiName(t1.name)
             in
                AvmMemberProperty(name,
                                  Some(
                                    AvmClass.CreateMemberMethod(name,
                                                                index1,
                                                                cp,
                                                                classNames,
                                                                methodInfo.[index1],
                                                                methodBodies,
                                                                t1.attributes)),
                                  Some(
                                    AvmClass.CreateMemberMethod(name,
                                                                index2,
                                                                cp,
                                                                classNames,
                                                                methodInfo.[index2],
                                                                methodBodies,
                                                                t2.attributes)))
         | [t2&{traitType = TraitSetter(_,index2)} ;
            t1&{traitType = TraitGetter(_,index1)}] ->
             let name = cp.SolveMultiName(t1.name)
             in
                AvmMemberProperty(name,
                                  Some(
                                    AvmClass.CreateMemberMethod(name,
                                                                index1,
                                                                cp,
                                                                classNames,
                                                                methodInfo.[index1],
                                                                methodBodies,
                                                                t1.attributes)),
                                  Some(
                                    AvmClass.CreateMemberMethod(name,
                                                                index2,
                                                                cp,
                                                                classNames,
                                                                methodInfo.[index2],
                                                                methodBodies,
                                                                t2.attributes)))
         | _ -> raise (new System.InvalidOperationException("Invalid getter/setter found"))


   static member Create(classInfo:AbcClassInfo, 
                        instanceInfo:AbcInstanceInfo,
                        methodInfo : AbcMethodInfo array,
                        methodBodies : AbcMethodBodyInfo array,
                        cp:ConstantPoolInfo,
                        classNames : QualifiedName array) :AvmClass=
       let theName = cp.SolveMultiName(instanceInfo.name) in
       let superClassName = cp.SolveMultiName(instanceInfo.superName) in
       let protectedNs = if instanceInfo.HasProtectedNs then
                            Some <| cp.SolveNamespaceName(instanceInfo.protectedNs)                            
                         else
                            None
       let methods =         
          (List.foldBack
                  (fun t r -> match t.traitType with   
                              | TraitMethod(_,index) -> 
                                   AvmClass.CreateMemberMethod(
                                           cp.SolveMultiName(t.name),
                                           index,
                                           cp, 
                                           classNames,
                                           methodInfo.[index],
                                           methodBodies,
                                           t.attributes
                                       )  :: r                                    
                              | _ -> r)
                      (Array.toList (instanceInfo.traits))
                      [])
       let properties =
          Array.toList instanceInfo.traits
            |> List.filter (fun t -> match t.traitType with
                                     | TraitGetter(_,_) -> true
                                     | TraitSetter(_,_) -> true
                                     | _ -> false) 
            |> Utils.MultiPartition (fun t1 t2 -> t1.name = t2.name)
            |> List.map (fun p -> AvmClass.CreateMemberProperty(
                                           p,
                                           cp,
                                           classNames, 
                                           methodInfo,
                                           methodBodies))
                               
              
       let slots =         
          (List.foldBack
                      (fun t r -> match t.traitType with   
                                  | TraitSlot(_,_,_,_) -> 
                                     AvmMemberSlot.Create(t.name,
                                                     t.traitType,
                                                     cp)  :: r                                    
                                  | _ -> r)
                      (Array.toList (instanceInfo.traits))
                      [])
       in AvmClass(theName,
                   superClassName,
                   AvmMethod.Create(methodInfo.[instanceInfo.iInit],
                                    cp,
                                    (Array.tryFind(fun(e:AbcMethodBodyInfo) -> e.MethodId = instanceInfo.iInit) methodBodies),
                                     classNames),
                   AvmMethod.Create(methodInfo.[classInfo.cInit],
                                    cp,
                                    (Array.tryFind(fun(e:AbcMethodBodyInfo) -> e.MethodId = classInfo.cInit) methodBodies),
                                    classNames),
                   slots,
                   methods,
                   properties,
                   protectedNs)
   interface AvmType 
end



type AvmScript(initMethod:AvmMethod,members:AvmClassMember list) =
  class
     member this.InitMethod 
        with get() = initMethod
     member this.Members 
        with get() = members
         
     static member Create(info: AbcScriptInfo, 
                          methodInfo : AbcMethodInfo array,
                          methodBodies : AbcMethodBodyInfo array,
                          instances:AbcInstanceInfo array,
                          constantPool:ConstantPoolInfo,
                          classNames : QualifiedName array) :  AvmScript =
         
         let initMethod = 
                AvmMethod.Create(methodInfo.[info.init],
                                 constantPool,
                                 (Array.tryFind(
                                         fun(e:AbcMethodBodyInfo) -> 
                                                   e.MethodId = info.init) methodBodies),  
                                 classNames)
         let slots =         
               (List.foldBack
                      (fun t r -> match t.traitType with   
                                  | TraitSlot(sId,_,_,_) -> 
                                     (sId,(AvmMemberSlot.Create(t.name,
                                                     t.traitType,
                                                     constantPool) :> AvmClassMember)) :: r                                    
                                  | TraitClass(sId,classIndex) ->
                                     let nameIndex = instances.[classIndex].name in
                                       (sId,(AvmMemberClassMember(constantPool.SolveMultiName(nameIndex)) :> AvmClassMember)) :: r                                     
                                  | _ -> r)
                      (Array.toList (info.traits))
                      [])   
             
         AvmScript(initMethod,
                   slots |> 
                     List.sortWith (fun (sId1,_) (sId2,_) ->                                  
                                   if sId1 = sId2 
                                    then   0 
                                    else if sId1 > sId2 
                                            then 1 
                                            else -1  )  |> 
                     List.map (fun (_,t) -> t))
                       
         
     member this.ToLowerIr(fileCreator : AbcFileCreator) =            
         let methodEntry = fileCreator.AddMethodInformationWithBody(initMethod.ToLowerIr(fileCreator)) in
              { init = methodEntry; 
                traits = (List.append 
                            (List.mapi 
                               (fun  i (m:AvmClassMember) -> 
                                      match m with 
                                         | ( :? AvmMemberSlot as slot) ->
                                                  {   
                                                     name = fileCreator.ConstantPoolCreator.GetNameEntry(slot.Name);
                                                     traitType = TraitSlot(i+1,fileCreator.ConstantPoolCreator.GetNameEntry(slot.Type),0,byte(0))
                                                     metadata = [||] // TODO Implement this
                                                     attributes = AbcTraitAttribute.Nothing
                                                  }
                                         | ( :? AvmMemberClassMember as classReference) ->
                                               let nameIndex = fileCreator.ConstantPoolCreator.GetNameEntry(classReference.ClassName) in
                                                  {   
                                                     name = nameIndex;
                                                     traitType = TraitClass(i+1,fileCreator.GetClassIndexForNameIndex( nameIndex ));
                                                     metadata = [||] // TODO Implement this
                                                     attributes = AbcTraitAttribute.Nothing
                                                  }
                                         | _ -> raise (new System.NotImplementedException("script trait not supported "+m.GetType().ToString()))
                                ) members ) [] ) |> List.toArray }
end


type AvmAbcFile(scripts: AvmScript list,classes : AvmClass list) = 
   class
   
   member this.Scripts 
      with get() = scripts

   member this.Classes
      with get() = classes
   
   member this.ToLowerIr(fileCreator : AbcFileCreator) =         
      List.iter (fun (c:AvmClass) -> let _ =  fileCreator.AddClass( c.ToLowerIr(fileCreator)) in ()) classes
      List.iter (fun (s:AvmScript) ->  let _ = fileCreator.AddScript( s.ToLowerIr(fileCreator)) in ()) scripts
      fileCreator.CreateFile()
   
   static member Create(stream : System.IO.Stream) = 
      let reader = new System.IO.BinaryReader(stream) in
      let file = AbcFile.ReadFrom(reader) in
      let classNames = 
           file.Instances |> Array.map 
              (fun (i:AbcInstanceInfo) -> 
                   file.ConstantPool.SolveMultiName(i.name)) in
      AvmAbcFile((Array.map (fun script -> AvmScript.Create(script,
                                                            file.Methods,
                                                            file.MethodBodies,
                                                            file.Instances,
                                                            file.ConstantPool,
                                                            classNames))
                            file.Scripts) |> Array.toList,
                 (Array.mapi (fun i value -> AvmClass.Create(file.Classes.[i],
                                                             file.Instances.[i],
                                                             file.Methods,
                                                             file.MethodBodies,
                                                             file.ConstantPool,
                                                             classNames))
                            file.Classes) |> Array.toList)
                                                           
   
end

module AvmActivePatterns = begin

  let (|AvmScript|)(script:AvmScript) = 
          script.InitMethod, script.Members

  let (|AvmMethod|)(m:AvmMethod) = 
      m.Name,m.ReturnType,m.Parameters,m.Body

  let (|AvmMethodBody|)(body:AvmMethodBody) =
             body.MaxStack,
             body.LocalCount,
             body.InitScopeDepth,
             body.MaxScopeDepth,
             body.Instructions,
             body.Exceptions,
             body.Traits

  let (|AvmClass|)(aclass:AvmClass) =
             (aclass.Name,
              aclass.SuperClassName,              
              aclass.Initializer,
              aclass.ClassInitializer,
              aclass.Slots,
              aclass.Methods,
              aclass.Properties,
              aclass.ProtectedNamespace ) 

  let (|AvmAbcFile|)(s:AvmAbcFile) = s.Scripts,s.Classes
end
